package com.gitee.taotaojs.util.reflect;

import com.gitee.taotaojs.exception.MyInnerException;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 反射工具类
 *
 * @author TaoTaojs
 * @author TaoTaojs
 * @date 2020/7/2 4:24 PM
 * <p>Description</p>
 * 直接获取反射方法对象
 * 运行反射方法对象与批量运行
 * 属性名加锁
 * 关联类:
 * 参考链接：
 * History: <br/>
 * <author>        <time>                      <version>          <desc>
 * TaoTaojs        2020/7/2 4:24 PM                V1.0           支持获取反射方法对象并直接运行，批量运行某方法，属性名加锁
 */

public final class ReflectUtil {

    private ReflectUtil(){}

    /**
     * 获取该类型的get方法对象
     * @param clazz 对象类类型
     * @param fieldName 属性名称
     * @param <T> 对象泛型
     * @return 属性对象
     */
    public static<T> Method getGetMethod(Class<T> clazz, String fieldName){
        return getMethod(clazz, MethodUtil.getMethodName(fieldName));
    }

    /**
     * 获取该类型的get方法对象
     * @param clazz 对象类类型
     * @param fieldName 属性名称
     * @param classes 方法传参
     * @param <T> 对象泛型
     * @return 实现对象
     */
    public static<T> Method getSetMethod(Class<T> clazz, String fieldName, Class<?> classes){
        return getMethod(clazz, MethodUtil.setMethodName(fieldName), classes);
    }

    /**
     * 直接创建方法对象
     * @param clazz 对象类类型
     * @param methodName 方法名称
     * @param classes 方法传参
     * @param <T> 对象泛型
     * @return 方法对象
     */
    public static<T> Method getMethod(Class<T> clazz, String methodName, Class<?>... classes){
        try {
            return clazz.getMethod(methodName, classes);
        } catch (NoSuchMethodException e) {
            throw new MyInnerException(e);
        }
    }

    /**
     * 使用方法名称直接执行方法
     * @param obj 对象实例
     * @param methodName 方法名称
     * @param classes 方法传参
     * @param objs 传参变长数组
     * @param <T> 对象泛型
     * @return 执行的返回值
     */
    public static<T> T invokeMethod(T obj, String methodName, Class[] classes, T[] objs){
        Method method = getMethod(obj.getClass(), methodName, classes);
        return invokeMethod(obj, method, objs);
    }

    /**
     * 直接运行无参方法
     * @param obj 对象实例
     * @param methodName 方法名称
     * @param <T> 对象泛型
     * @return 方法对象
     */
    public static<T> T invokeMethod(T obj, String methodName){
        return invokeMethod(obj, methodName, null, null);
    }
    @SuppressWarnings("checkstyle:MissingJavadocMethod")
    public static<T, V> T invokeMethod(T obj, Method method, V... objs){
        try {
            return (T) method.invoke(obj, objs);
        } catch (Exception e) {
            throw new MyInnerException(e);
        }
    }
    @SuppressWarnings("checkstyle:MissingJavadocMethod")
    public static<T> T invokeGet(T obj, Field field){
        try {
            return (T) field.get(obj);
        } catch (IllegalAccessException e) {
            throw new MyInnerException(e);
        }
    }
    @SuppressWarnings("checkstyle:MissingJavadocMethod")
    public static void invokeSet(Object obj, Field field, Object objs){
        try {
            field.set(obj, objs);
        } catch (IllegalAccessException e) {
            throw new MyInnerException(e);
        }
    }

    @SuppressWarnings("checkstyle:MissingJavadocMethod")
    public static<T> T invokeGet(T obj, String fieldName){
        return invokeMethod(obj, getGetMethod(obj.getClass(), fieldName));
    }
    @SuppressWarnings("checkstyle:MissingJavadocMethod")
    public static<T, V> T invokeSet(T obj, String fieldName, V object){
        return invokeMethod(obj, getSetMethod(obj.getClass(), fieldName, object.getClass()), object);
    }
    /** set的属性类型是面向接口时使用，例：setData(Map)，object的类型假设是linkedHashMap，如果不传clazz，将会找不到setData(linkedHashMap)这个方法 */
    @SuppressWarnings("checkstyle:MissingJavadocMethod")
    public static<T, V> T invokeSet(T obj, String fieldName, V object, Class<V> clazz){
        return invokeMethod(obj, getSetMethod(obj.getClass(), fieldName, clazz), object);
    }

    /**
     * 批量执行集合内所有对象的某个方法
     * @param clazz 对象类类型
     * @param objList 对象集合
     * @param methodName 方法名称
     * @param classes 传参类型
     * @param objs 传参
     * @param <T> 对象泛型
     * @return 原来的集合
     */
    public static<T> List<T> invokeMethodList(Class<T> clazz, List<T> objList, String methodName, Class[] classes, Object[] objs){
        List<T> list = new ArrayList<>();
        Method method = getMethod(clazz, methodName, classes);
        for (Object o : objList) {
            invokeMethod(o, method, objs);
        }
        return list;
    }
    @SuppressWarnings("checkstyle:MissingJavadocMethod")
    public static<T> void invokeMethodList(List<T> list, Method method, Object[] objs){
        for (Object o : list) {
            invokeMethod(o, method, objs);
        }
    }

    /**
     * 批量解锁/加锁属性字段
     * @param flag true加锁
     * @param fields 属性对象变长参数
     */
    public static void fieldAccess(boolean flag, Field... fields){
        for (Field field : fields) {
            field.setAccessible(flag);
        }
    }


    /**
     * 获取类类型中所有属性列表
     * @param clazz 类类型
     * @return 属性列表
     */
    public static PropertyDescriptor[] getPropertyList(Class<?> clazz){
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(clazz);
        } catch (IntrospectionException e) {
            throw new MyInnerException(e);
        }
        return beanInfo.getPropertyDescriptors();
    }


}
